package com.ctu.gzero.edit
{
	import com.ctu.gzero.compose.Composer;
	import com.ctu.gzero.compose.TxLine;
	import com.ctu.gzero.elements.DOMElement;
	import com.ctu.gzero.elements.ParaElement;
	import com.ctu.gzero.utils.TextRangeUtil;
	
	import flash.display.InteractiveObject;
	import flash.events.FocusEvent;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.events.TextEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.engine.TextBlock;
	import flash.text.engine.TextLine;
	import flash.ui.Keyboard;
	import flash.ui.Mouse;
	import flash.ui.MouseCursor;
	
	
	public class SelectionManager implements ISelectionManager
	{
		private var _dom:DOMElement;
		private var _composer:Composer;
		private var _anchorPosition:int = 0;
		private var _activePosition:int = 0;
		private var _endFlag:Boolean = false;
		private var allowOperationMerge:Boolean;
		private var _mouseOverSelectionArea:Boolean = false;
		
		public function SelectionManager()
		{
		}
		
		public function get selectionState():SelectionState
		{
			return new SelectionState(this._dom, this._anchorPosition, this._activePosition, this._endFlag);
		}
		
		public function get anchorPosition():int
		{
			return _anchorPosition;	
		}
		
		public function set anchorPosition(val:int):void
		{
			_anchorPosition = val;
		}
		
		public function get activePosition():int
		{
			return _activePosition;	
		}
		
		public function set activePosition(val:int):void
		{
			_activePosition = val;
		}
		
		public function get dom():DOMElement
		{
			return _dom;
		}

		public function set dom(val:DOMElement):void
		{
			_dom = val;
			_composer = _dom.composer;
		}
		
		public function focusInHandler(event:FocusEvent):void
		{
			var selectionState:SelectionState = new SelectionState(_dom, _anchorPosition, _activePosition);
			_composer.showSelection(selectionState);
		}
		
		public function textInputHandler(event:TextEvent):void
		{
			//EditManager must override this method
		}
		
		private function handleLeftArrow(event:KeyboardEvent):SelectionState
		{
			//TODO: no shift/ctrl/alt key is processed yet 
			event.stopPropagation();
			var selectionState:SelectionState = this.selectionState;
			TextRangeUtil.previousCharacter(selectionState, event.shiftKey);
			
			return selectionState;
		}
		
		private function handleUpArrow(event:KeyboardEvent):SelectionState
		{
			//TODO: no shift/ctrl/alt key is processed yet
			event.stopPropagation();
			var selectionState:SelectionState = this.selectionState;
			TextRangeUtil.previousLine(selectionState, event.shiftKey);
			return selectionState;
		}
		
		private function handleRightArrow(event:KeyboardEvent):SelectionState
		{
			//TODO: no shift/ctrl/alt key is processed yet
			event.stopPropagation();
			var selectionState:SelectionState = this.selectionState;
			TextRangeUtil.nextCharacter(selectionState, event.shiftKey);
			return selectionState;
		}
		
		private function handleDownArrow(event:KeyboardEvent):SelectionState
		{
			//TODO: no shift/ctrl/alt key is processed yet
			event.stopPropagation();
			var selectionState:SelectionState = this.selectionState;
			TextRangeUtil.nextLine(selectionState, event.shiftKey);
			return selectionState;
		}
		
		private function handleHomeKey(event:KeyboardEvent):SelectionState
		{
			//TODO: no shift/ctrl/alt key is processed yet
			event.stopPropagation();
			var selectionState:SelectionState = this.selectionState;
			TextRangeUtil.startOfLine(selectionState, event.shiftKey);
			return selectionState;
		}
		
		private function handleEndKey(event:KeyboardEvent):SelectionState
		{
			//TODO: no shift/ctrl/alt key is processed yet
			event.stopPropagation();
			var selectionState:SelectionState = this.selectionState;
			TextRangeUtil.endOfLine(selectionState, event.shiftKey);
			return selectionState;
		}
		
		private function handlePageDownKey(event:KeyboardEvent):SelectionState
		{
			var selectionState:SelectionState = this.selectionState;
			
			return selectionState;
		}
		
		private function handlePageUpKey(event:KeyboardEvent):SelectionState
		{
			var selectionState:SelectionState = this.selectionState;
			
			return selectionState;
		}
		
		private function handleKeyEvent(event:KeyboardEvent):void
		{
			var selectionState:SelectionState; 
			switch(event.keyCode)
            {
                case Keyboard.LEFT:
                {
                    selectionState = this.handleLeftArrow(event);
                    break;
                }
                case Keyboard.UP:
                {
                    selectionState = this.handleUpArrow(event);
                    break;
                }
                case Keyboard.RIGHT:
                {
                    selectionState = this.handleRightArrow(event);
                    break;
                }
                case Keyboard.DOWN:
                {
                    selectionState = this.handleDownArrow(event);
                    break;
                }
                case Keyboard.HOME:
                {
                    selectionState = this.handleHomeKey(event);
                    break;
                }
                case Keyboard.END:
                {
                    selectionState = this.handleEndKey(event);
                    break;
                }
                case Keyboard.PAGE_DOWN:
                {
                     selectionState = this.handlePageDownKey(event);
                    break;
                }
                case Keyboard.PAGE_UP:
                {
                    selectionState = this.handlePageUpKey(event);
                    break;
                }
                default:
                {
                    break;
                }
            }
            
            if(selectionState)
            	updateSelectionAndShape(selectionState);
		}
		
		protected function updateSelectionAndShape(selectionState:SelectionState):void
		{
			this.setSelection(selectionState);
			_composer.showSelection(selectionState);
		}
		
		public function internalSetSelection(selectionState:SelectionState) : void
        {
            var anchor:int = selectionState.anchorPosition;
			var active:int = selectionState.activePosition;
			var endFlag:Boolean = selectionState.endFlag;
			
			if(active != _activePosition)
			{
				this.clearSelectionShapes();
				
				if(active!=-1)
				{
					if(anchor<0)
					{
						anchor = 0;
						endFlag = false;
					}
					else if(anchor>=this._dom.textLength)
					{
						anchor = this._dom.textLength-1;
						endFlag = false;
					}
					
					if(active<0)
					{
						active = 0;
						endFlag = false;
					}
					else if(active>=this._dom.textLength)
					{
						active = this._dom.textLength-1;
						endFlag = false;
					}
				}
			}
	        this.setSelection(new SelectionState(_dom,anchor,active,endFlag));	        
	        this.selectionChanged();
	        this.allowOperationMerge = false;           
        }
		
		public function setSelection(selectionState:SelectionState):void
		{			
			this._anchorPosition = selectionState.anchorPosition;
			this._activePosition = selectionState.activePosition;
			this._endFlag = selectionState.endFlag;
		}
		
		public function keyDownHandler(event:KeyboardEvent):void
		{
			//trace("keyDownHandler:"+event);
			if (event.charCode == 0)
            {
                switch(event.keyCode)
                {
                    case Keyboard.LEFT:
                    case Keyboard.RIGHT:
                    case Keyboard.UP:
                    case Keyboard.DOWN:
                    case Keyboard.HOME:
                    case Keyboard.END:
                    case Keyboard.PAGE_DOWN:
                    case Keyboard.PAGE_UP:
                    {
                        this.handleKeyEvent(event);
                        break;
                    }// end case
                    default:
                    {
                        break;
                    }// end default
                }// end switch
            }// end if
		}
		
		public function keyUpHandler(event:KeyboardEvent):void
		{
			//trace("keyUpHandler:"+event);
		}
		
		public function mouseDownHandler(event:MouseEvent):void
		{
			//trace("mouseDownHandler:"+event);	
			
			var _selection:Boolean = this.hasSelection();
			
			if(event.shiftKey)
			{	
				if(this.setNewSelectionPoint(event,true))
				{
					this.selectionChanged();
					
					if(_selection)
					{
						this.clearSelectionShapes();
					}
					
					if(this.hasSelection())
					{
						this.addSelectionShapes();
					}
				}			
			}
			else
			{
				if(this.setNewSelectionPoint(event))
				{
					this.selectionChanged();
					trace(this._anchorPosition);
					if(_selection)
					{
						this.clearSelectionShapes();	
					}
					
					if(this.hasSelection())
					{
						this.addSelectionShapes();
					}					
					
				}
			}
			this.allowOperationMerge = false;			
		}
		
		public function mouseUpHandler(event:MouseEvent):void
		{
			//trace("mouseUpHandler:"+event);	
			if (!this._mouseOverSelectionArea)
            {
                Mouse.cursor = MouseCursor.AUTO;
            }
		}
		
		public function mouseMoveHandler(event:MouseEvent):void
		{
			//trace("mouseMoveHandler:"+event);	]
			var _selection:Boolean = this.hasSelection();
			
			if(event.buttonDown)
			{
				if(this.hasSelection())
				{
					if(this.setNewSelectionPoint(event,true))
					{
						this.selectionChanged();
						if(_selection)
						{
							this.clearSelectionShapes();
						}
						if(this.hasSelection())
						{
							this.addSelectionShapes();
						}
					}
				}
				else 
				{
					if(this.setNewSelectionPoint(event))
					{
						this.selectionChanged();
						if(_selection)
						{
							this.clearSelectionShapes();
						}
						if(this.hasSelection())
						{
							this.addSelectionShapes();
						}
					}
				}
				this.allowOperationMerge = false;
			}			
		}
		
		public function mouseOverHandler(event:MouseEvent):void
		{
			this._mouseOverSelectionArea = true;
			Mouse.cursor = MouseCursor.IBEAM;
		}
		
		public function mouseOutHandler(event:MouseEvent):void
		{
			this._mouseOverSelectionArea = false;
			Mouse.cursor = MouseCursor.AUTO;
		}
		
		public function rollOverHandler(event:MouseEvent):void
		{
			//trace("rollOverHandler:"+event);	
		}
		
		public function rollOutHandler(event:MouseEvent):void
		{
			//trace("rollOutHandler:"+event);
		}
		
		public function hasSelection():Boolean
		{			
			return (_activePosition!=-1);
		}
		
		public function selectionChanged(var1:Boolean=true,pointFormatChanged:Boolean=false):void
		{            
			if(pointFormatChanged)
			{
				//this.pointFormat = null;
			}
			
			if(var1)
			{
				//_dom.dispatchEvent(new SelectionEvent(SelectionEvent.SELECTION_CHANGE, false, false, this.hasSelection() ? ElementRange.createElementRange(this.dom,this._anchorPosition,this._activePosition):(null)));
				if(selectionState)
            	updateSelectionAndShape(selectionState);
			}
		}
		
		public function clearSelectionShapes():void
		{	
            if(this.dom.composer)
            {
            	this.dom.composer.hideSelection();
            }
		}
		
		public function addSelectionShapes():void
		{				
          	if(_activePosition != _anchorPosition)
          	{
          		var selectionState:SelectionState = new SelectionState(_dom, _anchorPosition, _activePosition, _endFlag);
          		_composer.showSelection(selectionState);
          	}
          	 
		}
		
		public function setNewSelectionPoint(event:MouseEvent, area:Boolean=false):Boolean
		{
			var _selPoint:* = this.selectionPoint(event.currentTarget, event.target as InteractiveObject, event.localX, event.localY, area);			
			if(_selPoint==null)
			{
				return false;
			}
			
			if(_selPoint.activePosition != _activePosition)
			{	
				this.internalSetSelection(_selPoint);				
				return true;
			}
			return false; 
		}
		
		private function selectionPoint(target:Object, activeTarget:InteractiveObject, x:Number, y:Number, area:Boolean = false) : SelectionState
        {
        	var endFlag:Boolean = false;
        	
        	if(!_dom)
        	{
        		return null;
        	}
        	
        	if(!this.hasSelection())
        	{
        		area = false;
        	}
        	
        	var anchorPosition:* = _anchorPosition;
        	var activePosition:* = _activePosition;
        	
        	activePosition = computeActivePositionIndex(_dom,activeTarget,target,x,y);
        	endFlag = computeActivePositionIsEnd(_dom,activeTarget,target, x, y);
        	
        	if(activePosition==-1)
        	{
        		return null;
        	}
        	
        	if(!area)
        	{
        		anchorPosition = activePosition;
        	}
        	
        	return new SelectionState(_dom, anchorPosition, activePosition, endFlag);
        }
        
        private function computeActivePositionIndex(dom:DOMElement, activeTarget:Object,target:Object,x:Number, y:Number):int
        {        	
        	var _textLine:TextLine;
        	var _tLine:TextLine;
        	var _textBlock:TextBlock;        	
        	var _para:ParaElement;
        	var _textRun:Boolean;
        	var _p:Point = new Point(x,y);
        	var _position:int = 0;        	
        	var atomIndex:int;
			var atomBound:Rectangle;
        	
			if (activeTarget is TextLine)
            {
            	_textLine = activeTarget as TextLine;        	
            	
            	_p = _textLine.localToGlobal(_p);            	
                atomIndex = _textLine.getAtomIndexAtPoint(_p.x,_p.y);
                atomBound = _textLine.getAtomBounds(atomIndex);
                
                _textBlock = _textLine.textBlock;
                _para = _textLine.userData.para;
                _position = _para.absoluteStart;
                
                _tLine = _textBlock.firstLine;
                while(_tLine)
                {
                	if(_tLine!=_textLine) 
                	{
                		_position += _tLine.rawTextLength;
                	} else {
                		_position += atomIndex;
                		_p = _textLine.globalToLocal(_p);
                		if((_p.x - atomBound.x) > (atomBound.x + atomBound.width - _p.x))
                		{ 
                			_position += 1;
                		}
                		break;
                	}    
                	_tLine = _tLine.nextLine;            	
                }                       
  
				_textLine.flushAtomData();              
            } 
            else  
            {
            	trace("canvas or graphic element");
            	_position=-1;
            }
        	return _position;
        }
        
        public function computeActivePositionIsEnd(dom:DOMElement, activeTarget:Object,target:Object,x:Number, y:Number):Boolean
        {
        	var _textLine:TextLine;
        	var _p:Point = new Point(x,y);
        	var atomIndex:int;
        	var absoluteAtomIndex:int;
			var atomBound:Rectangle;			
			var _endFlag:Boolean = false;
			var _txLine:TxLine = null;			
        	
        	if (activeTarget is TextLine)
            {
            	_textLine = activeTarget as TextLine;   
            	_txLine = _textLine.userData.txLine;
            	
            	var lastOffset:int = _txLine.textLength -1;
            	var _textLineOffset:int = _txLine.textLength - _textLine.rawTextLength;
            	
            	_p = _textLine.localToGlobal(_p);            	
                atomIndex = _textLine.getAtomIndexAtPoint(_p.x,_p.y);                
                absoluteAtomIndex = _textLineOffset + atomIndex;
                
                if(absoluteAtomIndex == lastOffset) 
                {
	                atomBound = _textLine.getAtomBounds(atomIndex);                
	                _p = _textLine.globalToLocal(_p);
	        		if((_p.x - atomBound.x) > (atomBound.x + atomBound.width - _p.x)  )
	        		{ 
	        			_endFlag = true;
	        		}
                }
        		
            }
            return _endFlag;
        }
	}
}